Changes for Ziggurat 0.2.0#3
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## dev #3 +/- ##
==========================================
Coverage 100.00% 100.00%
==========================================
Files 2 5 +3
Lines 644 1412 +768
==========================================
+ Hits 644 1412 +768 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
The wire-protocol mismatch that motivated tcp-adaptor-support-legacy-protocol is being fixed on the zigpy-ziggurat side instead (zigpy/zigpy-ziggurat#3), so this points back at the real PR branch. Pins to a commit via --rev rather than tracking --branch, since Docker's layer cache only invalidates when an ARG value actually changes, and a moving branch name would otherwise risk silently reusing a stale build across rebuilds. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
@puddly Do you think the protocol is already stable enough to make a |
|
I, uh, have another PR incoming that modifies the wire format yet again 😅. It should be the final breaking change for a while, all future changes should be additive and backwards-compatible via reserved bitmap flags. I'm hoping to get everything finalized and released before Wednesday since that is the Home Assistant Core beta cutoff. |
There was a problem hiding this comment.
Pull request overview
This PR aggregates the host-side changes needed for the upcoming Ziggurat 0.2.0 release by introducing a new binary control protocol, adding new transport options (binary WebSocket + Spinel tunnel), and updating the zigpy radio integration to use the new request/confirm callback model.
Changes:
- Added generated binary wire types and a higher-level binary protocol API (requests/responses/notifications, encoding helpers, errors).
- Implemented transport layer supporting binary WebSocket, legacy JSON WebSocket transcoding, and serial (Spinel tunnel) connectivity.
- Updated the zigpy
ControllerApplicationintegration for the new send-confirm model, packet capture support, tunables, and counter restore behavior; expanded tests accordingly.
Reviewed changes
Copilot reviewed 13 out of 14 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| zigpy_ziggurat/zigbee/wire.py | Adds generated binary wire structs/enums used by the new protocol layer. |
| zigpy_ziggurat/zigbee/transport.py | Introduces WebSocket + Spinel transports and legacy JSON transcoding. |
| zigpy_ziggurat/zigbee/protocol.py | Defines the binary protocol request/response/notification API and helpers. |
| zigpy_ziggurat/zigbee/legacy.py | Extends/adjusts legacy JSON models to support added fields and packet capture. |
| zigpy_ziggurat/zigbee/application.py | Migrates zigpy radio integration to the new API, send/confirm flow, and packet capture. |
| zigpy_ziggurat/zigbee/api.py | Implements a transport-agnostic request/stream/confirm API over binary frames. |
| zigpy_ziggurat/config.py | Adds Ziggurat-specific config schema (tunables). |
| pyproject.toml | Adds aiospinel dependency for Spinel tunnel support. |
| uv.lock | Locks aiospinel and updates dependency metadata. |
| tests/test_transport.py | Adds transport probe/transport behavior coverage (binary/legacy/spinel). |
| tests/test_protocol.py | Adds tests for protocol convenience accessors and round-trips. |
| tests/test_application.py | Updates application tests for new counters, send behavior, and packet capture. |
| tests/test_api.py | Reworks API tests around a fake binary transport and confirms/cancel behavior. |
| tests/common.py | Extends synthetic servers to include binary WebSocket and Spinel RCP fixtures/helpers. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| vol.Optional(CONF_TUNABLES, default={}): vol.Schema( | ||
| {vol.Optional(str): int} | ||
| ) |
| def _connection_lost(self, exc: BaseException | None) -> None: | ||
| for pending in self._pending.values(): | ||
| if not pending.response.done(): | ||
| pending.response.set_exception(ConnectionError("Connection lost")) | ||
| self._pending.clear() |
| async def _probe_websocket(url: str, on_frame: OnFrame, on_lost: OnLost) -> Transport: | ||
| """Pick the transport from the server's opening hello: binary frame or JSON text.""" | ||
| session, websocket = await _open_websocket(url) | ||
| async with asyncio.timeout(HANDSHAKE_TIMEOUT): | ||
| hello = await websocket.receive() | ||
|
|
| def __init__(self, config: dict[str, Any]) -> None: | ||
| super().__init__(config) | ||
| self._api: ZigguratApi | None = None | ||
|
|
| # The backup carries no capability, so device type is Unknown (restored as a | ||
| # sleepy end device); children without a known NWK address can't be loaded. | ||
| entries = [ | ||
| p.ChildEntry( | ||
| ieee=ieee, | ||
| nwk=network_info.nwk_addresses[ieee], | ||
| rx_on_when_idle=t.uint1_t(1), | ||
| device_type=p.ChildDeviceType.UNKNOWN, | ||
| reserved=t.uint5_t(0), |
| asdu: bytes, | ||
| route_control: RouteControl = RouteControl.STACK_DECIDES, | ||
| next_hop: t.NWK | None = None, | ||
| relays: list[t.NWK] | None = None, | ||
| ) -> SendUnicast: | ||
| return cls( |
Hehe, I figured. That's why I asked.
There's a small chance we'll be able to ship a beta |
This is a (WIP) aggregate PR for the changes required to support the upcoming Ziggurat 0.2.0 release. Notable features: